#coding:utf-8

'''
WebCrawlerGui
version 1.0
author:zheng lingjie
'''
import sys
import threading
import wx                  # This module uses the new wx namespace
from WebCrawler import WebCrawler
from Indexer import Indexer

import inspect
import ctypes  


#借助ctypes，强制终止一个运行中的python线程
def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    if not inspect.isclass(exctype):
        raise TypeError("Only types can be raised (not instances)")
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble, 
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")


class WebCrawlerGui(wx.Frame):

    def __init__(self, parent, id):
        
        self.crawlerThread = None
        self.indexerThread = None
        self.indexer = None
        self.crawler = None
        
        wx.Frame.__init__(self, parent, id, u'WebCrawlerGui', size = (600, 600))        
        #创建面板  
        self.panel = wx.Panel(self)
        
        #vbox:垂直方向放置控件的容器
        vbox = wx.BoxSizer(wx.VERTICAL)
        
        #---------------------------------网络爬虫-------------------------
        
        self.input_label = wx.StaticText(self.panel, -1, "请输入种子url:")
        self.input_text = wx.TextCtrl(self.panel, -1, 'http://www.boost.org/', size=(175, -1))
        self.input_text.SetInsertionPoint(0)
        
        vbox.Add(self.input_label,flag=wx.EXPAND|wx.TOP|wx.BOTTOM,border=5) 
        vbox.Add(self.input_text,flag=wx.EXPAND|wx.TOP|wx.BOTTOM,border=5)
        
        #在Panel上添加Button  
        self.button_crawl = wx.Button(self.panel, label = u'启动爬虫', size = (120, 30))
        self.Bind(wx.EVT_BUTTON, self.OnClick_crawl, self.button_crawl)
        vbox.Add(self.button_crawl, flag=wx.EXPAND|wx.TOP,border=5)
        
        #---------------------------------建立倒排索引-------------------------
        
        self.input_label_docs_path = wx.StaticText(self.panel, -1, "请输入文档集合的目录(暂时要求该目录需在应用程序根目录下):")
        self.input_text_docs_path = wx.TextCtrl(self.panel, -1, 'crawled_at_2014-08-11_09-48', size=(175, -1))
        self.input_text_docs_path.SetInsertionPoint(0)
        
        vbox.Add(self.input_label_docs_path,flag=wx.EXPAND|wx.TOP,border=30) 
        vbox.Add(self.input_text_docs_path,flag=wx.EXPAND|wx.TOP,border=5)
        
        self.button_buildIndex = wx.Button(self.panel, label = u'建立倒排索引', size = (120, 30))
        self.Bind(wx.EVT_BUTTON, self.OnClick_build_index, self.button_buildIndex)
        vbox.Add(self.button_buildIndex, flag=wx.EXPAND|wx.TOP,border=5)
        
        #---------------------------------查询界面-------------------------
        
        self.input_label_search = wx.StaticText(self.panel, -1, "请输入查询语句:")
        self.input_text_search = wx.TextCtrl(self.panel, -1, 'boost', size=(175, -1))
        self.input_text_search.SetInsertionPoint(0) 
        
        vbox.Add(self.input_label_search,flag=wx.EXPAND|wx.TOP,border=30) 
        vbox.Add(self.input_text_search,flag=wx.EXPAND|wx.TOP,border=5)
        
        #在Panel上添加Button  
        self.button_search_word = wx.Button(self.panel, label = u'查询单词', size = (120, 30))
        self.Bind(wx.EVT_BUTTON, self.OnClick_search_word, self.button_search_word)
        self.button_search_sentence = wx.Button(self.panel, label = u'查询句子', size = (120, 30))
        self.Bind(wx.EVT_BUTTON, self.OnClick_search_sentence, self.button_search_sentence)  
                
        #FlexGridSizer：以网格方式放置控件(每行两个控件，横向、竖向间隔30)
        sizer = wx.FlexGridSizer(cols=2, hgap=30, vgap=10)
        sizer.AddMany([self.button_search_word, self.button_search_sentence])  
        
        #将sizer放到vbox内
        vbox.Add(sizer,flag=wx.CENTRE|wx.TOP|wx.EXPAND,border=5)
        
        #---------------------------------查询结果显示界面------------------------- 
        ########## 多行文本框 ##########        
        self.search_result_text = wx.TextCtrl(self.panel, -1, u'查询结果', size=(600, 200), style=(wx.TE_MULTILINE | wx.TE_AUTO_SCROLL | wx.TE_DONTWRAP))  
        self.search_result_text.SetInsertionPoint(0)
        
        vbox.Add(self.search_result_text, flag=wx.CENTRE|wx.TOP|wx.EXPAND, border=15)
        
        
        #---------------------------------设置主窗口的显示界面为vbox-------------------------        
        self.panel.SetSizer(vbox)  
                 
           
    def OnClick_crawl(self, event):
        if self.button_crawl.GetLabel() == u'启动爬虫':            
            self.button_crawl.SetLabel("stop it?")
            #self.button_crawl.Disable()
            
            self.crawlerThread = CrawlerThread(self.input_text.GetValue().encode('utf-8'), self)
            self.crawlerThread.start()            
        elif self.button_crawl.GetLabel() == 'stop it?':
            #判断线程是否还在运行，若为true则强制终止线程
            if self.crawlerThread.isAlive():
                self.crawlerThread.terminate()
                self.button_crawl.SetLabel("finished!")
                self.button_crawl.Disable()                           

        
    def OnClick_build_index(self, event):
        if self.button_buildIndex.GetLabel() == u'建立倒排索引':            
            self.button_buildIndex.SetLabel("please wait!")
            self.button_buildIndex.Disable()
            
            #注意：GetValue()返回的是unicode编码的字符串，需转换为utf-8编码
            self.indexerThread = IndexerThread(self.input_text_docs_path.GetValue().encode('utf-8'), self)
            self.indexerThread.start()            
        else:
            pass
    
    def OnClick_search_word(self, event):
        if self.indexer:
            #注意：GetValue()返回的是unicode编码的字符串，需转换为utf-8编码。最后将输入转换为小写形式
            url_list = self.indexer.query_word(self.input_text_search.GetValue().encode('utf-8').lower())
            url_list_temp = []
            for url in url_list:            
                url_list_temp.append(url[0] + ' 出现次数：' + str(url[1]))
            
            result_str = '\n'.join(url_list_temp)
            self.search_result_text.SetValue(result_str)
        
    
    def OnClick_search_sentence(self, event):
        if self.indexer:
            url_list = self.indexer.query_sentence(self.input_text_search.GetValue().encode('utf-8').lower())
            
            result_str = '\n'.join(url_list)
            self.search_result_text.SetValue(result_str)
         


class CrawlerThread(threading.Thread):
    
    def __init__(self, url, parent):
        threading.Thread.__init__(self)
        self.__url = url
        self.__parent = parent
        
    def run(self):
        self.__parent.crawler = WebCrawler(self.__url)
        self.__parent.crawler.setRE(['/.*'], mode='ADD')
        self.__parent.crawler.work()
        
        #爬取完毕
        self.__parent.button_crawl.SetLabel("finnished!")
        self.__parent.button_crawl.Disable()
    
    def _get_my_tid(self):
        """determines this (self's) thread id"""
        if not self.isAlive():
            raise threading.ThreadError("the thread is not active")
 
        # do we have it cached?
        if hasattr(self, "_thread_id"):
            return self._thread_id
 
        # no, look for it in the _active dict
        for tid, tobj in threading._active.items():
            if tobj is self:
                self._thread_id = tid
                return tid
 
        raise AssertionError("could not determine the thread's id")                     
    
    def raise_exc(self, exctype):
        """raises the given exception type in the context of this thread"""
        _async_raise(self._get_my_tid(), exctype)
        
    def terminate(self):
        """raises SystemExit in the context of the given thread, which should 
        cause the thread to exit silently (unless caught)"""
        self.raise_exc(SystemExit)

class IndexerThread(threading.Thread):
    
    def __init__(self, path, parent):
        threading.Thread.__init__(self)
        self.__path = path
        self.__parent = parent
        
    def run(self):
        self.__parent.indexer = Indexer(self.__path)
        self.__parent.indexer.buildIndex()
        
        #建立索引完毕
        self.__parent.button_buildIndex.SetLabel("finnished!")
        self.__parent.button_buildIndex.Disable()


if __name__ == '__main__':
    app = wx.App()
    
    p = WebCrawlerGui(None,-1)
    p.Show()    
    app.MainLoop()